package org.yu.api.register;

import com.mojang.datafixers.util.Function4;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.Container;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.SlotItemHandler;
import net.minecraftforge.items.wrapper.PlayerInvWrapper;
import org.jetbrains.annotations.Nullable;

import javax.annotation.Nonnull;
import java.util.concurrent.atomic.AtomicReference;

public class LazyItemMenu extends AbstractContainerMenu {
        protected final IItemHandler playerInv;
        protected final FriendlyByteBuf extraData;
        protected final Player player;
        protected final Level level;
        protected final ItemStack itemStack;

        public LazyItemMenu(@Nullable MenuType<?> pMenuType, int pContainerId, Inventory playerInv, @Nonnull FriendlyByteBuf extraData) {
            super(pMenuType, pContainerId);
            this.playerInv = new PlayerInvWrapper(playerInv);
            this.extraData = extraData;
            this.player = playerInv.player;
            this.level = player.level;
            this.itemStack=player.getItemInHand(extraData.readBoolean()? InteractionHand.MAIN_HAND:InteractionHand.OFF_HAND);
        }


    /**
         * @return 屏幕是否能够开启
         */
        @Override
        public boolean stillValid(@Nonnull Player player) {
            return player.isAlive();
        }

        /**
         * 将ItemHandler的某个格子转化为slot
         *
         * @param index 格子索引
         * @param x     屏幕坐标X
         * @param y     屏幕坐标Y
         * @return SlotItemHandler，可用于addSlotBox
         */
        public SlotItemHandler newSlot(int index, int x, int y) {
            AtomicReference<SlotItemHandler> result = new AtomicReference<>();
            itemStack.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).ifPresent(iItemHandler -> {
                result.set(new SlotItemHandler(iItemHandler, index, x, y));
            });
            return result.get();
        }

        @Override
        public String toString() {
            return "LazyMenu{" +
                    "playerInv=" + playerInv +
                    ", extraData=" + extraData +
                    ", player=" + player +
                    ", level=" + level +
                    '}';
        }

        /**
         * Places the player inventory slots into the container.
         *
         * @param leftCol The x coordinate of the top left slot
         * @param topRow  The y coordinate of the top left slot
         */
        protected void layoutPlayerInventorySlots(int leftCol, int topRow) {
            this.addSlotBox(this.playerInv, 9, leftCol, topRow, 9, 18, 3, 18);
            topRow += 58;
            this.addSlotRange(this.playerInv, 0, leftCol, topRow, 9, 18);
        }

        /**
         * Adds a box of slots to the container
         *
         * @param handler   The inventory of the slot
         * @param index     The index of the first slot
         * @param x         The x coordinate of the top left slot
         * @param y         The y coordinate of the top left slot
         * @param horAmount The amount of slots in horizontal direction
         * @param dx        The space between two slots in horizontal direction. Should not be less than 16 or
         *                  you create overlapping slots. Most of the time this is 18
         * @param verAmount The amount of slots in vertical direction
         * @param dy        The space between two slots in vertical direction. Should not be less than 16 or
         *                  you create overlapping slots. Most of the time this is 18
         * @return The next index to be used to create a slot
         */
        protected int addSlotBox(IItemHandler handler, int index, int x, int y, int horAmount, int dx, int verAmount, int dy) {
            return this.addSlotBox(handler, index, x, y, horAmount, dx, verAmount, dy, SlotItemHandler::new);
        }

        /**
         * Adds a row of slots to the container
         *
         * @param handler The inventory of the slot
         * @param index   The index of the first slot
         * @param x       The x coordinate of the top left slot
         * @param y       The y coordinate of the top left slot
         * @param amount  The amount of slots
         * @param dx      The space between two slots. Should not be less than 16 or
         *                you create overlapping slots. Most of the time this is 18
         * @return The next index to be used to create a slot
         */
        protected int addSlotRange(IItemHandler handler, int index, int x, int y, int amount, int dx) {
            return this.addSlotRange(handler, index, x, y, amount, dx, SlotItemHandler::new);
        }

        /**
         * Adds a box of slots to the container
         *
         * @param handler     The inventory of the slot
         * @param index       The index of the first slot
         * @param x           The x coordinate of the top left slot
         * @param y           The y coordinate of the top left slot
         * @param horAmount   The amount of slots in horizontal direction
         * @param dx          The space between two slots in horizontal direction. Should not be less than 16 or
         *                    you create overlapping slots. Most of the time this is 18
         * @param verAmount   The amount of slots in vertical direction
         * @param dy          The space between two slots in vertical direction. Should not be less than 16 or
         *                    you create overlapping slots. Most of the time this is 18
         * @param slotFactory A factory to create a slot. This could be {@code SlotItemHandler::new}
         *                    or {@code SlotOutputOnly::new} for output slots.
         * @return The next index to be used to create a slot
         */
        protected int addSlotBox(IItemHandler handler, int index, int x, int y, int horAmount, int dx, int verAmount, int dy, Function4<IItemHandler, Integer, Integer, Integer, Slot> slotFactory) {
            for (int j = 0; j < verAmount; j++) {
                index = this.addSlotRange(handler, index, x, y, horAmount, dx, slotFactory);
                y += dy;
            }
            return index;
        }

        /**
         * Adds a row of slots to the container
         *
         * @param handler     The inventory of the slot
         * @param index       The index of the first slot
         * @param x           The x coordinate of the top left slot
         * @param y           The y coordinate of the top left slot
         * @param amount      The amount of slots
         * @param dx          The space between two slots. Should not be less than 16 or
         *                    you create overlapping slots. Most of the time this is 18
         * @param slotFactory A factory to create a slot. This could be {@code SlotItemHandler::new}
         *                    or {@code SlotOutputOnly::new} for output slots.
         * @return The next index to be used to create a slot
         */
        protected int addSlotRange(IItemHandler handler, int index, int x, int y, int amount, int dx, Function4<IItemHandler, Integer, Integer, Integer, Slot> slotFactory) {
            for (int i = 0; i < amount; i++) {
                this.addSlot(slotFactory.apply(handler, index, x, y));
                x += dx;
                index++;
            }
            return index;
        }

        // As opposed to the super method this checks for Slot#mayPlace(ItemStack)
        @Override
        protected boolean moveItemStackTo(@Nonnull ItemStack stack, int startIndex, int endIndex, boolean reverseDirection) {
            boolean success = false;

            if (stack.isStackable()) {
                int idx = reverseDirection ? endIndex - 1 : startIndex;
                while (!stack.isEmpty()) {
                    if (reverseDirection ? idx < startIndex : idx >= endIndex) {
                        break;
                    }

                    Slot slot = this.slots.get(idx);
                    ItemStack content = slot.getItem();
                    if (!content.isEmpty() && ItemStack.isSameItemSameTags(stack, content) && slot.mayPlace(stack)) {
                        int totalCount = content.getCount() + stack.getCount();
                        int maxSize = Math.min(slot.getMaxStackSize(), stack.getMaxStackSize());
                        if (totalCount <= maxSize) {
                            stack.setCount(0);
                            content.setCount(totalCount);
                            slot.setChanged();
                            success = true;
                        } else if (content.getCount() < maxSize) {
                            stack.shrink(maxSize - content.getCount());
                            content.setCount(maxSize);
                            slot.setChanged();
                            success = true;
                        }
                    }

                    idx += (reverseDirection ? -1 : 1);
                }
            }

            if (!stack.isEmpty()) {
                int idx = reverseDirection ? endIndex - 1 : startIndex;
                while (true) {
                    if (reverseDirection ? idx < startIndex : idx >= endIndex) {
                        break;
                    }

                    Slot slot = this.slots.get(idx);
                    ItemStack content = slot.getItem();
                    if (content.isEmpty() && slot.mayPlace(stack)) {
                        if (stack.getCount() > slot.getMaxStackSize()) {
                            slot.set(stack.split(slot.getMaxStackSize()));
                        } else {
                            slot.set(stack.split(stack.getCount()));
                        }
                        slot.setChanged();
                        success = true;
                        break;
                    }

                    idx += (reverseDirection ? -1 : 1);
                }
            }
            return success;
        }


    @Override
    public ItemStack quickMoveStack(Player pPlayer, int pIndex) {
        return ItemStack.EMPTY;
    }
}
